﻿using Dto;
using Entity;
using Furion.DatabaseAccessor;
using System.Collections.Generic;
using System.Linq;

namespace Logics;


/// <summary>
/// 拼团全局逻辑
/// </summary>
public class Teambuy : ISingleton
{
    private readonly ISqlSugarClient db;

    private readonly Points point;

    private readonly Logics.MoneyLogics money;

    private static readonly Random _random = new Random();

    /// <summary>
    /// 
    /// </summary>
    /// <param name="db"></param>
    /// <param name="point"></param>
    /// <param name="money"></param>
    public Teambuy(ISqlSugarClient db, Points point, Logics.MoneyLogics money)
    {
        this.db = db;
        this.point = point;
        this.money = money;
    }

    /// <summary>
    /// 结束拼团
    /// </summary>
    /// <param name="teamId"></param>
    /// <returns></returns>
    public async Task End(int teamId)
    {
        try
        {
            // 开启数据库事务
            await db.Ado.BeginTranAsync();

            // 查询拼团信息
            var teamBuy = await db.Queryable<Entity.TeamBuy>().FirstAsync(x => x.Id == teamId);

            //人数不足不处理
            if (teamBuy.CurrentNum != teamBuy.MaxNum || teamBuy.State != TeamBuyState.进行中) return;

            //获取拼团商品信息
            var teamBuyProduct = await db.Queryable<Entity.TeamBuyProduct>().FirstAsync(x => x.Id == teamBuy.ProductId);

            //获取所有参与用户 以及其累计的红包金额
            var users = await GetTeamUserLogAndMoney(teamId, teamBuy.ProductId);

            //获取成功和失败的用户
            var (successUsers, failUsers) = RandomWinner(users, teamBuyProduct.MaxPacketPrice);

            //处理成功用户
            await HandlerSuccess(successUsers, teamBuy, teamBuyProduct.Point);

            //处理失败用户
            await HandlerFail(failUsers, teamBuy, teamBuyProduct.MaxPacketPrice, teamBuyProduct.OnePacketPrice);

            teamBuy.State = TeamBuyState.已结束;

            await db.Updateable(teamBuy).ExecuteCommandHasChangeAsync();

            //提交事务
            await db.Ado.CommitTranAsync();
        }
        catch (Exception ex)
        {
            // 发生异常时回滚事务
            await db.Ado.RollbackTranAsync();

            // 记录错误日志
            Furion.Logging.Log.Error($"结束拼团失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 获取参团用户和其历史的红包金额
    /// </summary>
    /// <param name="teamBuyId"></param>
    /// <param name="productId"></param>
    /// <returns></returns>
    public async Task<List<Entity.TeamBuyLog>> GetTeamUserLogAndMoney(int teamBuyId, int productId)
    {
        string sql = @$"SELECT IFNULL(b.money, 0) AS HistoryMoney,  IFNULL(b.number, 0) AS HistoryCount, a.*  FROM teambuylog AS a
                    LEFT JOIN (SELECT SUM(money) AS money, SUM(number) AS number, userid FROM teambuyuserredpacketlog where productid = {productId} GROUP BY userid) AS b 
                    ON a.userid = b.userid
                    WHERE a.teambuyid = {teamBuyId};";

        return await db.SqlQueryable<Entity.TeamBuyLog>(sql).ToListAsync();
    }

    /// <summary>
    /// 随机获取1个拼团成功用户
    /// </summary>
    /// <returns></returns>
    private (List<TeamBuyLog>, List<TeamBuyLog>) RandomWinner(List<TeamBuyLog> users, decimal maxPrice)
    {
        // 机器人列表
        var robots = users.Where(x => x.IsRobot).ToList();

        //真实用户 红包从大到小排序
        var realUsers = users.Where(x => x.IsRobot == false).OrderByDescending(e => e.HistoryMoney).ToList();

        // 成功用户列表
        var successUsers = new List<TeamBuyLog>();

        // 失败用户列表
        var failUsers = new List<TeamBuyLog>();

        //是否第一次拼团，判断条件 所有真实用户的红包金额都是0
        bool isFirst = realUsers.Where(x => x.HistoryMoney == 0).Count() == realUsers.Count;

        //第一次拼团并且团中有机器人,只出机器人
        if (isFirst && robots.Count > 0)
        {
            successUsers.AddRange(robots);

            failUsers.AddRange(realUsers);
        }
        //不是第一次拼团
        else
        {
            //将所有的的历史金额进行降序
            var historyInfo = realUsers.OrderByDescending(x => x.HistoryMoney).OrderByDescending(x => x.Id).ToList();

            //达到条件的用户数量
            var meetUserCount = realUsers.Where(x => x.HistoryCount >= 2 || x.HistoryMoney >= maxPrice).Count();

            //有达到条件的用户
            if (meetUserCount > 0 || robots.Count == 0)
            {
                var winner = historyInfo.First();

                successUsers.Add(winner);

                realUsers.Remove(winner);

                failUsers.AddRange(realUsers);
            }
            //移除所有机器人 ，出所有真人
            else
            {
                failUsers.AddRange(realUsers);
            }
        }

        return (successUsers, failUsers);

    }

    /// <summary>
    /// 处理成功用户
    /// </summary>
    /// <param name="users"></param>
    /// <param name="teamBuy"></param>
    /// <param name="winnerPoint"></param>
    /// <returns></returns>
    private async Task HandlerSuccess(List<TeamBuyLog> users, Entity.TeamBuy teamBuy, decimal winnerPoint)
    {
        var userIds = users.Select(e => e.UserId).ToList();

        //将所有用户标记为成功用户
        users.ForEach(e => e.IsWinner = true);

        //中奖奖品信息
        var prizes = new List<Entity.TeamBuyPrize>();

        //红包领取记录
        var moneyLog = await db.Queryable<Entity.TeambuyUserRedPacketLog>().Where(x => userIds.Contains(x.UserId)).ToListAsync();

        //清空累计次数和金额
        foreach (var item in moneyLog)
        {
            item.Number = 0;

            item.Money = 0;
        }

        //创建奖品
        foreach (var user in users)
        {
            if (user.IsRobot) continue;

            prizes.Add(new TeamBuyPrize
            {
                ProductId = teamBuy.ProductId,

                StoreId = teamBuy.StoreId,

                UserId = user.UserId,

                TeambuyId = teamBuy.Id,

                IsResolve = false,
            });

            //发送奖励积分
            await point.Add(user.UserId, winnerPoint, "拼团成功积分奖励");
        }

        //清空红包记录
        await db.Updateable(moneyLog).ExecuteCommandAsync();

        //插入奖品
        await db.Insertable(prizes).ExecuteCommandAsync();

        //更新用户状态
        await db.Updateable(users).ExecuteCommandAsync();
    }

    /// <summary>
    /// 处理失败用户
    /// </summary>
    /// <returns></returns>
    private async Task HandlerFail(List<TeamBuyLog> users, Entity.TeamBuy teamBuy, decimal maxRedpacktPrice, decimal oneRedpacketPrice)
    {
        var logs = new List<Entity.TeamBuyLog>();

        //随机红包金额
        var packets = RandomRedPackeet(oneRedpacketPrice, users.Count);

        for (int i = 0; i < users.Count; i++)
        {
            var user = users[i];

            //将用户标记为失败用户
            user.IsWinner = false;

            //获取一个随机的拼团ID
            int teamId = await RandomTeamId(teamBuy, users.Count);

            //将用户加入新的拼团
            logs.Add(new Entity.TeamBuyLog
            {
                JoinTime = DateTime.Now,

                TeamBuyId = teamId,

                NickName = user.NickName,

                Avatar = user.Avatar,

                UserId = user.UserId,

                StoreId = teamBuy.StoreId,

                ProxyId = teamBuy.ProxyId

            });

            //发送红包奖励
            await RedPacket(user.UserId, packets[i], teamBuy.ProductId, maxRedpacktPrice);
        }


        //插入新的拼团记录
        await db.Insertable(logs).ExecuteCommandAsync();

        //更新用户状态
        await db.Updateable(users).ExecuteCommandAsync();
    }

    /// <summary>
    /// 随机红包金额
    /// </summary>
    /// <param name="totalPrice">红包总金额</param>
    /// <param name="userCount">发送人数</param>
    /// <returns></returns>
    public List<decimal> RandomRedPackeet(decimal totalPrice, int userCount)
    {
        var result = new List<decimal>();

        // 如果总金额小于最小红包金额 * 人数，则每人分配最小红包金额
        if (totalPrice <= 2 * userCount)
        {
            for (int i = 0; i < userCount; i++)
            {
                result.Add(2);
            }
            return result;
        }

        // 剩余可分配金额
        decimal remainingMoney = totalPrice - (2 * userCount);
        // 剩余待分配人数
        int remainingPeople = userCount;

        // 使用二倍均值法进行随机分配
        for (int i = 0; i < userCount - 1; i++)
        {
            // 计算当前红包的最大可能金额
            decimal maxMoney = remainingMoney / remainingPeople * 2;

            // 生成随机红包金额（保留2位小数）
            decimal redPacket = Math.Round((decimal)(_random.NextDouble() * (double)(maxMoney)), 2);

            // 确保每个红包至少有2元
            redPacket += 2;

            result.Add(redPacket);

            // 更新剩余金额和人数
            remainingMoney -= (redPacket - 2);

            remainingPeople--;
        }

        // 最后一个红包为剩余金额
        result.Add(remainingMoney + 2);

        return result;
    }

    /// <summary>
    /// 发送奖励红包
    /// </summary>
    /// <returns></returns>
    private async Task RedPacket(int userId, decimal redPacket, int productId, decimal maxredPacket)
    {
        var log = await db.Queryable<Entity.TeambuyUserRedPacketLog>().FirstAsync(x => x.UserId == userId);

        if (log == null)
        {
            log = new TeambuyUserRedPacketLog
            {
                Money = redPacket,

                Number = 1,

                UserId = userId,

                ProductId = productId
            };

            await db.Insertable(log).ExecuteCommandAsync();
        }
        else
        {
            //判断红包金额是否超过最大值 如果超出只发2元固定红包
            if (log.Money > maxredPacket || log.Money + redPacket > maxredPacket) log.Money += 2;

            else log.Money += redPacket;

            log.Number += 1;

            await db.Updateable(log).ExecuteCommandHasChangeAsync();
        }

        //发送红包奖励
        await money.Client(new ChangeMoneyAndLog
        {
            Uid = userId,

            moneyType = MoneyType.拼团失败红包,

            Price = redPacket,

            Type = 1
        });
    }

    /// <summary>
    /// 获取一个随机可以加入的拼团ID
    /// </summary>
    /// <returns></returns>
    private async Task<int> RandomTeamId(Entity.TeamBuy data, int userCount)
    {
        // 查询所有符合条件的拼团
        var teams = await db.Queryable<Entity.TeamBuy>().Where(x => x.ProductId == data.ProductId && x.State == TeamBuyState.进行中 && x.CurrentNum < x.MaxNum).Select(x => x.Id).ToListAsync();

        // 如果没有符合条件的拼团创建一个新的拼团
        if (teams.Count == 0)
        {
            var newTeam = data.DeepCopy();

            newTeam.CurrentNum = userCount;

            newTeam.JoinRobot = false;

            newTeam.State = TeamBuyState.进行中;

            newTeam.EndTime = DateTime.Now;

            int newId = await db.Insertable(newTeam).ExecuteReturnIdentityAsync();

            return newId;
        }

        // 随机选择一个拼团
        var random = new Random();

        var randomTeamId = teams[random.Next(teams.Count)];

        return randomTeamId;
    }

    /// <summary>
    /// 给某个拼团添加机器人
    /// </summary>
    /// <param name="teamId"></param>
    /// <returns></returns>
    public async Task AddRobot(int teamId)
    {
        try
        {
            // 查询拼团信息
            var teamBuy = await db.Queryable<Entity.TeamBuy>().FirstAsync(x => x.Id == teamId);

            //真实用户数量
            var logCount = await db.Queryable<Entity.TeamBuyLog>().Where(x => x.TeamBuyId == teamId && x.IsRobot == false).CountAsync();

            //满员或者小于5个真人 不添加机器人
            if (teamBuy.CurrentNum == teamBuy.MaxNum || logCount < 5) return;

            // 计算剩余空位
            int remainingSlots = teamBuy.MaxNum - teamBuy.CurrentNum;

            // 如果没有剩余空位，直接返回
            if (remainingSlots <= 0) return;

            // 更新当前人数为最大人数
            teamBuy.CurrentNum = teamBuy.MaxNum;

            //标记已经录入过机器人
            teamBuy.JoinRobot = true;

            // 创建机器人记录列表
            var robotLogs = new List<TeamBuyLog>();

            // 循环添加机器人
            for (int i = 0; i < remainingSlots; i++)
            {
                // 获取随机名称和头像
                var (name, avatar) = await Api.NickName.Get();

                // 添加机器人记录
                robotLogs.Add(new TeamBuyLog
                {
                    UserId = 0,

                    NickName = name,

                    Avatar = avatar,

                    IsRobot = true,

                    IsWinner = false,

                    StoreId = teamBuy.StoreId,

                    ProxyId = teamBuy.ProxyId,

                    JoinTime = DateTime.Now,

                    TeamBuyId = teamId
                });
            }

            // 批量保存机器人记录
            await db.Insertable(robotLogs).ExecuteCommandAsync();

            // 更新拼团信息
            await db.Updateable(teamBuy).ExecuteCommandAsync();

        }
        catch (Exception ex)
        {
            // 记录错误日志
            Furion.Logging.Log.Error($"添加机器人失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 加入拼团
    /// </summary>
    /// <param name="teamId">拼团ID</param>
    /// <param name="userId">用户ID</param>
    /// <param name="nickName">用户昵称</param>
    /// <param name="avatar">用户头像</param>
    /// <returns>是否加入成功</returns>
    public async Task<bool> Join(int teamId, int userId, string nickName, string avatar)
    {
        try
        {
            // 开启数据库事务
            await db.Ado.BeginTranAsync();

            // 查询拼团信息，使用行锁
            var teamBuy = await db.Queryable<Entity.TeamBuy>().With(SqlWith.UpdLock).FirstAsync(x => x.Id == teamId);

            // 检查拼团状态
            if (teamBuy == null || teamBuy.State != TeamBuyState.进行中)
            {
                await db.Ado.RollbackTranAsync();

                return false;
            }

            // 检查是否已满
            if (teamBuy.CurrentNum >= teamBuy.MaxNum)
            {
                await db.Ado.RollbackTranAsync();
                return false;
            }

            // 检查用户是否已经加入过该拼团
            var existLog = await db.Queryable<Entity.TeamBuyLog>()
                .FirstAsync(x => x.TeamBuyId == teamId && x.UserId == userId);

            if (existLog != null)
            {
                await db.Ado.RollbackTranAsync();
                return false;
            }

            // 创建拼团记录
            var log = new Entity.TeamBuyLog
            {
                TeamBuyId = teamId,
                UserId = userId,
                NickName = nickName,
                Avatar = avatar,
                JoinTime = DateTime.Now,
                IsRobot = false,
                IsWinner = false,
                StoreId = teamBuy.StoreId,
                ProxyId = teamBuy.ProxyId
            };

            // 更新拼团人数
            teamBuy.CurrentNum += 1;

            // 保存拼团记录
            await db.Insertable(log).ExecuteCommandAsync();

            // 更新拼团信息
            await db.Updateable(teamBuy).ExecuteCommandAsync();

            // 提交事务
            await db.Ado.CommitTranAsync();

            return true;
        }
        catch (Exception ex)
        {
            // 发生异常时回滚事务
            await db.Ado.RollbackTranAsync();

            // 记录错误日志
            Furion.Logging.Log.Error($"加入拼团失败: {ex.Message}");

            return false;
        }
    }

}
